
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       soft_sigreader_int.c
  * @author     baiyang
  * @date       2022-6-22
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "soft_sigreader_int.h"

#include <board.h>
#include <rthw.h>

#if defined(HAL_USE_EICU)
/*-----------------------------------macro------------------------------------*/
#define USE_TMR6                        0

#if (USE_TMR6 == 1)
#define EXAMPLE_CAPT_IRQN               (INT002_IRQn)
#define EXAMPLE_CAPT_INT_SRC            (INT_SRC_TMR6_2_GCMP_A)

#define TMR6_2_PWMB_PORT                (GPIO_PORT_B)
#define TMR6_2_PWMB_PIN                 (GPIO_PIN_00)
#endif

/* TimerA unit and channel definitions for this example. */
#define TMRA_UNIT                       (CM_TMRA_3)
#define TMRA_CH                         (TMRA_CH3)
#define TMRA_PERIPH_CLK                 (FCG2_PERIPH_TMRA_3)

/*
 * Specify conditions for TimerA capturing in this example.
 * The conditions are included in @ref TMRA_Capture_Cond
 */
#define TMRA_CAPT_COND                  (TMRA_CAPT_COND_PWM_RISING | TMRA_CAPT_COND_PWM_FALLING)

/* Specifies the pin or the event to be used according to the capture-condition. */
#if (TMRA_CAPT_COND & (TMRA_CAPT_COND_PWM_RISING | TMRA_CAPT_COND_PWM_FALLING))
#define TMRA_CAPT_PWM_PORT              (GPIO_PORT_B)
#define TMRA_CAPT_PWM_PIN               (GPIO_PIN_00)
#define TMRA_CAPT_PWM_PIN_FUNC          (GPIO_FUNC_5)
#endif

/* Specifies clock divider that you need. @ref TMRA_Clock_Divider */
#define TMRA_CLK_DIV                    (TMRA_CLK_DIV64)

/* Definitions of interrupt. */
#define TMRA_INT_IRQn                   (INT002_IRQn)
#define TMRA_INT_SRC                    (INT_SRC_TMRA_3_CMP)
#define TMRA_INT_PRIO                   (DDL_IRQ_PRIO_03)
#define TMRA_INT_TYPE                   (TMRA_INT_CMP_CH3)
#define TMRA_INT_FLAG                   (TMRA_FLAG_CMP_CH3)
/*----------------------------------typedef-----------------------------------*/
typedef struct soft_sigreader_int* soft_sigreader_int_t;

#pragma pack(1)
typedef struct {
    uint16_t w0;
    uint16_t w1;
} ssi_pulse_t;
#pragma pack()

struct soft_sigreader_int {
    uint16_t head, tail;
    ssi_pulse_t sigbuf_pool[SOFTSIG_MAX_SIGNAL_TRANSITIONS];

    uint16_t last_value;
    float pulse_scale;     // tick -> us
};

/*---------------------------------prototype----------------------------------*/
#if (USE_TMR6 == 1)
static void Tmr6_2_CapInputCallBack(void);
static void ConfigCapture(CM_TMR6_TypeDef *TMR6x);
#endif

static void TmrAConfig(void);
static void TmrACaptureCondConfig(void);
static void TmrAIrqConfig(void);
static void TMRA_IrqCallback(void);
static float get_tmra_input_capture_time_scale(CM_TMRA_TypeDef *TMRAx);
/*----------------------------------variable----------------------------------*/
static struct soft_sigreader_int ssi;
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
static inline void sigbuf_init()
{
    ssi.head = ssi.tail = 0;
}

static inline void sigbuf_put(ssi_pulse_t *pulse)
{
    uint16_t next = (ssi.head + 1) % SOFTSIG_MAX_SIGNAL_TRANSITIONS;

    if (next != ssi.tail) {
        ssi.sigbuf_pool[ssi.head] = (*pulse);
        ssi.head = next;
    }
}

static inline void sigbuf_get(ssi_pulse_t *pulse)
{
    if (ssi.tail != ssi.head) {
        *pulse = ssi.sigbuf_pool[ssi.tail];
        ssi.tail = (ssi.tail + 1) % SOFTSIG_MAX_SIGNAL_TRANSITIONS;
    }
}

static inline uint16_t sigbuf_data_len()
{
    if (ssi.head == ssi.tail) {
        return 0;
    } else if (ssi.head > ssi.tail) {
        return ssi.head - ssi.tail;
    } else {
        return SOFTSIG_MAX_SIGNAL_TRANSITIONS - (ssi.tail - ssi.head);
    }
}

void ssi_init()
{
    sigbuf_init();

#if (USE_TMR6 == 1)
    FCG_Fcg2PeriphClockCmd(FCG2_PERIPH_TMR6_2, ENABLE);

    /* Timer6 PWM output pin configuration */
    GPIO_SetFunc(TMR6_2_PWMB_PORT, TMR6_2_PWMB_PIN, GPIO_FUNC_3);

    /* Config CM_TMR6_2 capture in */
    ConfigCapture(CM_TMR6_2);

    /* Start timer6 */
    TMR6_SWSyncStart(TMR6_SW_SYNC_U2);
#endif

    /* Configures TimerA. */
    TmrAConfig();

    /* Starts TimerA. */
    TMRA_Start(TMRA_UNIT);
}

bool ssi_read(uint32_t *widths0, uint32_t *widths1)
{
    /* disable interrupt */
    rt_base_t level = rt_hw_interrupt_disable();

    if (sigbuf_data_len() >= 2) {
        ssi_pulse_t pulse;
        sigbuf_get(&pulse);

        *widths0 = (uint32_t)(pulse.w1 * ssi.pulse_scale) + 1;
        *widths1 = (uint32_t)((pulse.w0 - pulse.w1) * ssi.pulse_scale) + 1;

        /* enable interrupt */
        rt_hw_interrupt_enable(level);
        return true;
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    return false;
}

/**
 * @brief  TimerA counter compare match interrupt callback function.
 * @param  None
 * @retval None
 */
static void TMRA_IrqCallback(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    /* A capture occurred */
    /* Get capture value by calling function TMRA_GetCompareValue. */
    uint32_t u32CaptureA = TMRA_GetCompareValue(TMRA_UNIT, TMRA_CH);

    if ((CM_GPIO->PIDRB & TMRA_CAPT_PWM_PIN) == 1) { //上升沿
        ssi_pulse_t pulse;

        pulse.w0 = u32CaptureA;
        pulse.w1 = ssi.last_value;

        sigbuf_put(&pulse);

        //check for missed interrupt
        if (TMRA_UNIT->BCSTR & (TMRA_FLAG_OVF | TMRA_FLAG_UDF)) {
            //we have missed some pulses
            //try to reset RCProtocol parser by returning invalid value (i.e. 0 width pulse)
            pulse.w0 = 0;
            pulse.w1 = 0;

            sigbuf_put(&pulse);

            //reset overcapture mask
            TMRA_UNIT->BCSTR &= (uint16_t)(~(TMRA_FLAG_OVF | TMRA_FLAG_UDF));
        }
    }

    ssi.last_value = u32CaptureA;

    TMRA_ClearStatus(TMRA_UNIT, TMRA_INT_FLAG);

    /* leave interrupt */
    rt_interrupt_leave();
}

/**
 * @brief  TimerA configuration.
 * @param  None
 * @retval None
 */
static void TmrAConfig(void)
{
    stc_tmra_init_t stcTmraInit;

    /* 1. Enable TimerA peripheral clock. */
    FCG_Fcg2PeriphClockCmd(TMRA_PERIPH_CLK, ENABLE);

    /* 2. Set a default initialization value for stcTmraInit. */
    (void)TMRA_StructInit(&stcTmraInit);

    /* 3. Modifies the initialization values depends on the application. */
    stcTmraInit.sw_count.u16ClockDiv = TMRA_CLK_DIV;
    (void)TMRA_Init(TMRA_UNIT, &stcTmraInit);

    /* 4. Set function mode as capturing mode. */
    TMRA_SetFunc(TMRA_UNIT, TMRA_CH, TMRA_FUNC_CAPT);

    /* 5. Configures the capture condition. */
    TmrACaptureCondConfig();

    /* 6. Configures IRQ if needed. */
    TmrAIrqConfig();

    ssi.pulse_scale = get_tmra_input_capture_time_scale(TMRA_UNIT);
}

/**
 * @brief  Capture condition configuration.
 * @param  None
 * @retval None
 */
static void TmrACaptureCondConfig(void)
{
    stc_gpio_init_t stcGpioInit;

    GPIO_StructInit(&stcGpioInit);
    stcGpioInit.u16PinDrv   = PIN_MID_DRV;
    stcGpioInit.u16PullUp   = PIN_PU_ON;
    GPIO_Init(TMRA_CAPT_PWM_PORT, TMRA_CAPT_PWM_PIN, &stcGpioInit);

    GPIO_SetFunc(TMRA_CAPT_PWM_PORT, TMRA_CAPT_PWM_PIN, TMRA_CAPT_PWM_PIN_FUNC);

    /* Capture input pin configuration */
    TMRA_SetFilterClockDiv(TMRA_UNIT, TMRA_CH, TMRA_FILTER_CLK_DIV16);
    TMRA_FilterCmd(TMRA_UNIT, TMRA_CH, ENABLE);

    /* Hardware capture: TimerA PWM3 pin rising */
    TMRA_HWCaptureCondCmd(TMRA_UNIT, TMRA_CH, TMRA_CAPT_COND, ENABLE);

    /* HW Clear: TimerA PWM3 pin fall edge */
    TMRA_HWClearCondCmd(TMRA_UNIT, TMRA_CLR_COND_PWM3_RISING, ENABLE);
}

/**
 * @brief  TimerA interrupt configuration.
 * @param  None
 * @retval None
 */
static void TmrAIrqConfig(void)
{
    stc_irq_signin_config_t stcIrq;

    stcIrq.enIntSrc    = TMRA_INT_SRC;
    stcIrq.enIRQn      = TMRA_INT_IRQn;
    stcIrq.pfnCallback = &TMRA_IrqCallback;
    (void)INTC_IrqSignIn(&stcIrq);

    NVIC_ClearPendingIRQ(stcIrq.enIRQn);
    NVIC_SetPriority(stcIrq.enIRQn, TMRA_INT_PRIO);
    NVIC_EnableIRQ(stcIrq.enIRQn);

    /* Enable the specified interrupts of TimerA. */
    TMRA_IntCmd(TMRA_UNIT, TMRA_INT_TYPE, ENABLE);
}

// tick -> us
static float get_tmra_input_capture_time_scale(CM_TMRA_TypeDef *TMRAx)
{
    uint32_t u32clkFreq;
    uint16_t u16Div;

    stc_clock_freq_t stcClockFreq;
    CLK_GetClockFreq(&stcClockFreq);

    if ((TMRAx == CM_TMRA_1) || \
        (TMRAx == CM_TMRA_2) || \
        (TMRAx == CM_TMRA_3) || \
        (TMRAx == CM_TMRA_4)) {
        u32clkFreq = stcClockFreq.u32Pclk0Freq;
    } else {
        u32clkFreq = stcClockFreq.u32Pclk1Freq;
    }

    u16Div = READ_REG16(TMRAx->BCSTR) & TMRA_BCSTR_CKDIV;

    switch (u16Div)
    {
        case (TMRA_CLK_DIV2):
            u32clkFreq /= 2;
            break;
        case (TMRA_CLK_DIV4):
            u32clkFreq /= 4;
            break;
        case (TMRA_CLK_DIV8):
            u32clkFreq /= 8;
            break;
        case (TMRA_CLK_DIV16):
            u32clkFreq /= 16;
            break;
        case (TMRA_CLK_DIV32):
            u32clkFreq /= 32;
            break;
        case (TMRA_CLK_DIV64):
            u32clkFreq /= 64;
            break;
        case (TMRA_CLK_DIV128):
            u32clkFreq /= 128;
            break;
        case (TMRA_CLK_DIV256):
            u32clkFreq /= 256;
            break;
        case (TMRA_CLK_DIV512):
            u32clkFreq /= 512;
            break;
        case (TMRA_CLK_DIV1024):
            u32clkFreq /= 1024;
            break;
        case (TMRA_CLK_DIV1):
        default:
            break;
    }
    return 1e6f / u32clkFreq;
}

#if (USE_TMR6 == 1)
/**
 * @brief  TIMER6 capture input interrupt handler callback.
 * @param  None
 * @retval None
 */
static void Tmr6_2_CapInputCallBack(void)
{
    uint32_t u32CaptureA = TMR6_GetCompareValue(CM_TMR6_2, TMR6_CMP_REG_A);
    DDL_Printf("0x%x\r\n", (unsigned int)u32CaptureA);

    if ((CM_GPIO->PIDRB & TMR6_2_PWMB_PIN) == 1) { //上升沿
        ssi_pulse_t pulse;

        pulse.w0 = u32CaptureA;
        pulse.w1 = ssi.last_value;

        sigbuf_put(&pulse);

        //check for missed interrupt
        if (CM_TMR6_2->STFLR & (TMR6_FLAG_OVF | TMR6_FLAG_UDF)) {
            //we have missed some pulses
            //try to reset RCProtocol parser by returning invalid value (i.e. 0 width pulse)
            pulse.w0 = 0;
            pulse.w1 = 0;

            sigbuf_put(&pulse);
            //reset overcapture mask
            CM_TMR6_2->STFLR &= (~(TMR6_FLAG_OVF));
        }
    }

    ssi.last_value = u32CaptureA;
}

/**
 * @brief  Config CM_TMR6_2 PWM capture input
 * @param  [in] TMR6x               Timer6 unit
 *  @arg CM_TMR6_x
 * @retval None
 */
static void ConfigCapture(CM_TMR6_TypeDef *TMR6x)
{
    stc_timer6_init_t stcTmr6Init;
    stc_irq_signin_config_t stcIrqRegiConf;

    (void)TMR6_StructInit(&stcTmr6Init);

    /* Timer6 general count function configuration */
    stcTmr6Init.sw_count.u32ClockDiv = TMR6_CLK_DIV16;
    stcTmr6Init.u32PeriodValue = 0xFFFFu;
    (void)TMR6_Init(TMR6x, &stcTmr6Init);

    /* Capture input pin configuration */
    (void)TMR6_SetFilterClockDiv(TMR6x, TMR6_IO_PWMA, TMR6_FILTER_CLK_DIV4);
    TMR6_FilterCmd(TMR6x, TMR6_IO_PWMA, ENABLE);
    TMR6_SetFunc(TMR6x, TMR6_CH_A, TMR6_PIN_CAPT_INPUT);

    /* Hardware capture: Timer6 PWMA pin rising */
    TMR6_HWCaptureCondCmd(TMR6x, TMR6_CH_A, TMR6_CAPT_COND_PWMA_RISING | TMR6_CAPT_COND_PWMA_FAILLING, ENABLE);
    /* HW Clear: Timer6 PWMA pin fall edge */
    TMR6_HWClearCondCmd(TMR6x, TMR6_CLR_COND_PWMA_RISING, ENABLE);
    TMR6_HWClearCmd(TMR6x, ENABLE);

    /* Enable CM_TMR6_2 GCMAR interrupt */
    TMR6_IntCmd(TMR6x, TMR6_INT_MATCH_A, ENABLE);
    stcIrqRegiConf.enIRQn = EXAMPLE_CAPT_IRQN;
    stcIrqRegiConf.enIntSrc = EXAMPLE_CAPT_INT_SRC;
    stcIrqRegiConf.pfnCallback = &Tmr6_2_CapInputCallBack;
    (void)INTC_IrqSignIn(&stcIrqRegiConf);
    NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
    NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIO_04);
    NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}
#endif
/*------------------------------------test------------------------------------*/
#endif

